package lumis.portal.serviceinterface;

import lumis.portal.PortalException;
import lumis.portal.UnexpectedException;
import lumis.portal.manager.ManagerException;
import lumis.portal.manager.ManagerFactory;
import lumis.portal.stability.StableMinor;
import lumis.util.IResource;
import lumis.util.XmlUtil;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;

import javax.portlet.PortletMode;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;

/**
 * Holds information about a service interface.
 * 
 * @since 4.0.0
 * @version $Revision: 13166 $ $Date: 2011-06-22 19:32:35 -0300 (Wed, 22 Jun 2011) $
 */
@StableMinor(version = "6.0", sinceVersion = "4.0")
public class ServiceInterfaceConfig
{
	private String id;
	private String serviceId;
	private String name;
	private String title;
	private String description;
	private String baseUrl;
	private List<String> stringsUrlPrefixes;
	private String resourceClassName;
	private IResource resource;
	private Node definitionNode;
	private String implementationClassName;
	private String interfaceType;
	private boolean isRuntimeOnly;
	private boolean isAdministration;
	private boolean isDetails;
	private boolean isList;
	private boolean isPrintable;
	private int renderAs;
	private boolean enableCache;
	private int cacheLevel;
	private int cachePersonalization;
	private boolean cacheServerSideInclude;
	private boolean previewSupported;
	private boolean serviceInstanceRequired;
	private boolean isWidget;
	private boolean inContextEditSupported;
		
	/**
	 * Stores an unmodifiable list of the supported modes in this interface.
	 * @since 4.0.7
	 */
	private List<PortletMode> supportedModes;
	
	// renderAs constants
	public static final String RENDER_AS_INLINE_DEFINITION_VALUE = "inline";
	public static final int RENDER_AS_INLINE_VALUE = 0;
	public static final String RENDER_AS_CLIENT_SIDE_SCRIPT_DEFINITION_VALUE = "clientSideScript";
	public static final int RENDER_AS_CLIENT_SIDE_SCRIPT_VALUE = 1;
	
	// cache/level constants
	public static final String CACHE_LEVEL_LOCAL_DEFINITION_VALUE = "local";
	public static final int CACHE_LEVEL_LOCAL_VALUE = 0;
	public static final String CACHE_LEVEL_PAGE_TEMPLATE_DEFINITION_VALUE = "pageTemplate";
	public static final int CACHE_LEVEL_PAGE_TEMPLATE_VALUE = 1;
	
	// cache/personalization constants
	public static final String CACHE_PERSONALIZATION_NONE_DEFINITION_VALUE = "none";
	public static final int CACHE_PERSONALIZATION_NONE_VALUE = 0;
	public static final String CACHE_PERSONALIZATION_GUEST_ONLY_DEFINITION_VALUE = "guestOnly";
	public static final int CACHE_PERSONALIZATION_GUEST_ONLY_VALUE = 1;
	public static final String CACHE_PERSONALIZATION_PER_USER_DEFINITION_VALUE = "perUser";
	public static final int CACHE_PERSONALIZATION_PER_USER_VALUE = 2;
	
	/**
	 * @return Returns the definition.
	 */
	public Node getDefinitionNode() throws PortalException
	{
		return this.definitionNode;
	}

	/**
	 * @return Returns the definition Node
	 */
	public String getDefinition() throws PortalException
	{
		if(this.definitionNode != null)
			return XmlUtil.getXmlString((Element) this.definitionNode);
		else
			return null;
	}

	/**
	 * @param definition
	 *            The definition to set.
	 */
	public void setDefinition(String interfaceDefinition) throws PortalException
	{
		if (interfaceDefinition != null)
		{
			Document interfaceDoc = XmlUtil.getXMLDocumentFromString(interfaceDefinition);
			setDefinitionNode(interfaceDoc.getDocumentElement());			
		}
		else
			definitionNode = null;
	}

	/**
	 * @param definition
	 *            The definition to set.
	 */
	public void setDefinitionNode(Node interfaceXmlNode) throws PortalException
	{
		this.definitionNode = interfaceXmlNode;
		this.serviceId = XmlUtil.getTextContent(interfaceXmlNode.getAttributes().getNamedItem("serviceId"));
		this.id = this.serviceId + "." + XmlUtil.getTextContent(interfaceXmlNode.getAttributes().getNamedItem("id"));
		this.name = XmlUtil.getTextContent(interfaceXmlNode.getAttributes().getNamedItem("name"));
		this.title = XmlUtil.readNodeString("title", interfaceXmlNode);
		this.description = XmlUtil.readNodeString("description", interfaceXmlNode);
		this.resourceClassName = XmlUtil.readNodeString("resource/className", interfaceXmlNode);
		this.implementationClassName = XmlUtil.readAttributeString("className", interfaceXmlNode);
		this.interfaceType = XmlUtil.readAttributeString("type", interfaceXmlNode);
		this.baseUrl = XmlUtil.readNodeString("baseUrl", interfaceXmlNode);
		this.isRuntimeOnly = XmlUtil.readNodeBoolean("isRuntimeOnly", interfaceXmlNode, false);
		this.isAdministration = XmlUtil.readNodeBoolean("isAdministration", interfaceXmlNode, false);
		this.isDetails = XmlUtil.readNodeBoolean("isDetails", interfaceXmlNode, false);
		this.isList = XmlUtil.readNodeBoolean("isList", interfaceXmlNode, false);
		this.isPrintable = XmlUtil.readNodeBoolean("isPrintable", interfaceXmlNode, true);
		this.previewSupported = XmlUtil.readNodeBoolean("previewSupported", interfaceXmlNode, false);
		this.serviceInstanceRequired = XmlUtil.readNodeBoolean("serviceInstanceRequired", interfaceXmlNode, true);
		this.isWidget = XmlUtil.readNodeBoolean("isWidget", interfaceXmlNode, false);
		this.inContextEditSupported = XmlUtil.readNodeBoolean("inContextEditSupported",
				interfaceXmlNode, true);
		
		if (id.endsWith(" "))
			throw new ManagerException ("STR_INVALID_IDENTIFIER_FOR_INTERFACE;" + id);
		
		String renderAsTemp = XmlUtil.readNodeString("renderAs", interfaceXmlNode, RENDER_AS_INLINE_DEFINITION_VALUE);
		if(renderAsTemp.equals(RENDER_AS_INLINE_DEFINITION_VALUE))
			this.renderAs = RENDER_AS_INLINE_VALUE;
		else if(renderAsTemp.equals(RENDER_AS_CLIENT_SIDE_SCRIPT_DEFINITION_VALUE))
			this.renderAs = RENDER_AS_CLIENT_SIDE_SCRIPT_VALUE;
		
		Node cacheNode = XmlUtil.selectSingleNode("cache",interfaceXmlNode);
		if(cacheNode != null)
			this.enableCache = XmlUtil.readAttributeBoolean("enable", cacheNode, false);
		else
			this.enableCache = false;
		
		String cacheLevelTemp = XmlUtil.readNodeString("cache/level", interfaceXmlNode, CACHE_LEVEL_LOCAL_DEFINITION_VALUE);
		if(cacheLevelTemp.equals(CACHE_LEVEL_LOCAL_DEFINITION_VALUE))
			this.cacheLevel = CACHE_LEVEL_LOCAL_VALUE;
		else if(cacheLevelTemp.equals(CACHE_LEVEL_PAGE_TEMPLATE_DEFINITION_VALUE))
			this.cacheLevel = CACHE_LEVEL_PAGE_TEMPLATE_VALUE;
		
		String cachePersonalizationTemp = XmlUtil.readNodeString("cache/personalization", interfaceXmlNode, CACHE_PERSONALIZATION_NONE_DEFINITION_VALUE);
		if(cachePersonalizationTemp.equals(CACHE_PERSONALIZATION_NONE_DEFINITION_VALUE))
			this.cachePersonalization = CACHE_PERSONALIZATION_NONE_VALUE;
		else if(cachePersonalizationTemp.equals(CACHE_PERSONALIZATION_GUEST_ONLY_DEFINITION_VALUE))
			this.cachePersonalization = CACHE_PERSONALIZATION_GUEST_ONLY_VALUE;
		else if(cachePersonalizationTemp.equals(CACHE_PERSONALIZATION_PER_USER_DEFINITION_VALUE))
			this.cachePersonalization = CACHE_PERSONALIZATION_PER_USER_VALUE;
		
		this.cacheServerSideInclude = XmlUtil.readNodeBoolean("cache/serverSideInclude", interfaceXmlNode, false);

		// load and prefix the stringsUrlPrefixes with baseUrl
		String[] arrStringsUrlPrefixes = XmlUtil.readNodesString("resource/stringsUrlPrefixes/stringsUrlPrefix", interfaceXmlNode);
		List<String> tempStringsUrl = new ArrayList<String>(arrStringsUrlPrefixes.length);
		for (int i = 0; i < arrStringsUrlPrefixes.length; i++)
			tempStringsUrl.add(this.baseUrl + "/" + arrStringsUrlPrefixes[i]);
		this.stringsUrlPrefixes = Collections.unmodifiableList(tempStringsUrl);
	
		// read supported modes
		Node[] modeNodes = XmlUtil.selectNodes("modes/mode", interfaceXmlNode);
		if (modeNodes.length > 0)
		{
			supportedModes = new ArrayList<PortletMode>(modeNodes.length);
			for (Node modeNode: modeNodes)
			{
				String mode = XmlUtil.getTextContent(modeNode);
				supportedModes.add(new PortletMode(mode));
			}
			supportedModes = Collections.unmodifiableList(supportedModes);
		}
		else
		{
			supportedModes = Collections.singletonList(PortletMode.VIEW);			
		}
	}

	/**
	 * @return Returns the id.
	 */
	public String getId()
	{
		return id;
	}

	public void setId(String id)
	{
		this.id = id;
	}
	
	public String getRelativeId()
	{
		return id.substring(id.lastIndexOf(".")+1);
	}

	/**
	 * @return Returns the id.
	 */
	public String getBaseUrl()
	{
		return baseUrl;
	}

	public void setBaseUrl(String baseUrl)
	{
		this.baseUrl = baseUrl;
	}

	/**
	 * @return Returns the stringsUrlPrefix array.
	 */
	public List<String> getStringsUrlPrefixes()
	{
		return this.stringsUrlPrefixes;
	}

	/**
	 * @return Returns the first (main) stringsUrlPrefix.
	 */
	public String getStringsUrlPrefix()
	{
		if (this.stringsUrlPrefixes == null || this.stringsUrlPrefixes.isEmpty())
			return null;
		else
			return this.stringsUrlPrefixes.get(0);
	}

	public void setStringsUrlPrefix(String stringsUrlPrefix)
	{
		if (stringsUrlPrefix != null)
			this.stringsUrlPrefixes = Collections.singletonList(stringsUrlPrefix);
		else
			this.stringsUrlPrefixes = null;
	}

	/**
	 * @return Returns the name.
	 */
	public String getName()
	{
		return name;
	}

	public void setName(String name)
	{
		this.name = name;
	}
	
	/**
	 * @return Returns the description.
	 */
	public String getDescription()
	{
		return description;
	}
	
	public void setDescription(String description)
	{
		this.description = description;
	}
	
	/**
	 * @return Returns the serviceId.
	 */
	public String getServiceId()
	{
		return serviceId;
	}
	
	public void setServiceId(String serviceId)
	{
		this.serviceId = serviceId;
	}
	
	public String getProperty(String name)
	{
		// TODO
		return null;
	}

	public Enumeration getProperties(String name)
	{
		// TODO
		return null;
	}

	public Enumeration getPropertyNames()
	{
		// TODO
		return null;
	}

	public String getExtendedProperty(String name)
	{
		// TODO
		return null;
	}

	public Enumeration getExtendedProperties(String name)
	{
		// TODO
		return null;
	}

	public Enumeration getExtendedPropertyNames()
	{
		// TODO
		return null;
	}

	public String getImplementationClass()
	{
		return implementationClassName;
	}

	public String getInterfaceType()
	{
		return interfaceType;
	}

	public IResource getResource() throws PortalException
	{
		try
		{
			if (this.resource == null)
			{
				synchronized (this)
				{
					if (this.resource == null)
					{
						// get the class object for resource class name
						// specified in the service definition
						Class<? extends IResource> resourceClass = ManagerFactory.getDeploymentManager()
								.getClassLoader().loadClass(resourceClassName).asSubclass(IResource.class);
	
						// instantiate the resource object
						this.resource = resourceClass.newInstance();
	
						// initialize resource object, adding string URL
						// prefixes
						for (String stringUrl: stringsUrlPrefixes)
							this.resource.addResourcePath(stringUrl);
					}
				}
			}
	
			return this.resource;
		}
		/*catch(PortalException e)
		{
			throw e;
		}*/
		catch(Exception e)
		{
			throw new UnexpectedException(e);
		}
	}

	public String getPopupWindowProperties() throws PortalException
	{
		return XmlUtil.readNodeString("popupWindowProperties", this.definitionNode, "width=600,height=500,resizable");
	}
	
	/**
	 * @return Returns the isRuntimeOnly.
	 */
	public boolean isRuntimeOnly()
	{
		return isRuntimeOnly;
	}
	/**
	 * @param isRuntimeOnly The isRuntimeOnly to set.
	 */
	public void setRuntimeOnly(boolean isRuntimeOnly)
	{
		this.isRuntimeOnly = isRuntimeOnly;
	}
	
	/**
	 * @return Returns the isAdministration.
	 */
	
	public boolean isAdministration()
	{
		return isAdministration;
	}
	/**
	 * @param isAdministration The isAdministration to set.
	 */
	public void setAdministration(boolean isAdministration)
	{
		this.isAdministration = isAdministration;
	}
	
	public boolean isDetails()
	{
		return isDetails;
	}
	public void setDetails(boolean isDetails)
	{
		this.isDetails = isDetails;
	}
	
	public boolean isList()
	{
		return isList;
	}
	
	public void isList(boolean isList)
	{
		this.isList = isList;
	}
	
	/**
	 * @return Returns the isPrintable.
	 */
	
	public boolean isPrintable()
	{
		return isPrintable;
	}
	/**
	 * @param isPrintable The isPrintable to set.
	 */
	public void setPrintable(boolean isPrintable)
	{
		this.isPrintable = isPrintable;
	}
	
	/**
	 * @return Returns the renderAs.
	 */
	
	public int getRenderAs()
	{
		return renderAs;
	}
	/**
	 * @param renderAs The renderAs to set.
	 */
	public void setRenderAs(int renderAs)
	{
		this.renderAs = renderAs;
	}
	
	/**
	 * @return Returns the enableCache.
	 */
	
	public boolean isEnableCache()
	{
		return enableCache;
	}
	/**
	 * @param enableCache The enableCache to set.
	 */
	public void setEnableCache(boolean enableCache)
	{
		this.enableCache = enableCache;
	}
	
	/**
	 * @return Returns the cacheLevel.
	 */
	
	public int getCacheLevel()
	{
		return cacheLevel;
	}
	/**
	 * @param cacheLevel The cacheLevel to set.
	 */
	public void setCacheLevel(int cacheLevel)
	{
		this.cacheLevel = cacheLevel;
	}
	
	/**
	 * @return Returns the cachePersonalization.
	 */
	
	public int getCachePersonalization()
	{
		return cachePersonalization;
	}
	/**
	 * @param cachePersonalization The cachePersonalization to set.
	 */
	public void setCachePersonalization(int cachePersonalization)
	{
		this.cachePersonalization = cachePersonalization;
	}
	
	/**
	 * @return Returns the cacheServerSideInclude.
	 */
	
	public boolean isCacheServerSideInclude()
	{
		return cacheServerSideInclude;
	}
	/**
	 * @param cacheServerSideInclude The cacheServerSideInclude to set.
	 */
	public void setCacheServerSideInclude(boolean cacheServerSideInclude)
	{
		this.cacheServerSideInclude = cacheServerSideInclude;
	}
	
	public String getTitle()
	{
		return title;
	}
	public void setTitle(String title)
	{
		this.title = title;
	}

	public boolean isPreviewSupported()
	{
		return previewSupported;
	}

	public void setPreviewSupported(boolean previewSupported)
	{
		this.previewSupported = previewSupported;
	}

	public boolean isInContextEditSupported()
	{
		return inContextEditSupported;
	}

	// method for JPA only
	@SuppressWarnings("unused")
	private void setInContextEditSupported(boolean inContextEditSupported)
	{
		this.inContextEditSupported = inContextEditSupported;
	}

	/**
	 * Indicates whether this interface requires or not a service instance to be
	 * executed.
	 * 
	 * @param serviceInstanceRequired
	 *            Indicates whether this interface requires or not a service
	 *            instance to be executed.
	 * @since 5.6.0
	 */
	public void setServiceInstanceRequired(boolean serviceInstanceRequired)
	{
		this.serviceInstanceRequired = serviceInstanceRequired;
	}

	/**
	 * Returns whether this interface requires or not a service instance to be
	 * executed.
	 * 
	 * @return whether this interface requires or not a service instance to be
	 *         executed.
	 * @since 5.6.0
	 */
	public boolean isServiceInstanceRequired()
	{
		return serviceInstanceRequired;
	}

	/**
	 * Returns a unmodifiable list with the supported modes for this interface.
	 * The list contains the modes in the same order they were defined in the
	 * interface definition.
	 * @return a unmodifiable list with the supported modes for this interface. 
	 * @since 4.0.7
	 */
	public List<PortletMode> getSupportedModes()
	{
		return supportedModes;
	}

	/**
	 * Sets the isWidget.
	 * @param isWidget the isWidget value to set.
	 * @since 6.0.0
	 */
	public void setIsWidget(boolean isWidget)
	{
		this.isWidget = isWidget;
	}

	/**
	 * Returns the isWidget.
	 * @return the isWidget
	 * @since 6.0.0
	 */
	public boolean getIsWidget()
	{
		return isWidget;
	}
}
